package com.yixing.tech.common.convert;

import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.stream.StreamSupport;

/**
 * 转换器接口函数
 *
 * @author yixing tech
 */
public interface Converter<S, T> extends Function<S, T> {

    /**
     * Accept the source type and target type or not
     *
     * @param sourceType the source type
     * @param targetType the target type
     * @return if accepted, return <code>true</code>, or <code>false</code>
     */
    default boolean accept(Class<?> sourceType, Class<?> targetType) {
        return getSourceType().isAssignableFrom(sourceType)
                && getTargetType().isAssignableFrom(targetType);
    }

    /**
     * Convert the source-typed value to the target-typed value
     *
     * @param source the source-typed value
     * @return the target-typed value
     */
    T convert(S source);

    @Override
    default T apply(S source) {
        return convert(source);
    }

    /**
     * Get the source type
     *
     * @return non-null
     */
    Class<S> getSourceType();

    /**
     * Get the target type
     *
     * @return non-null
     */
    Class<T> getTargetType();

    /**
     * Get the Converter instance from {@link ServiceLoader} with the specified source and target type
     *
     * @param sourceType the source type
     * @param targetType the target type
     * @return
     * @see ServiceLoader#load(Class)
     */
    static Converter<?, ?> getConverter(Class<?> sourceType, Class<?> targetType) {
       return StreamSupport.stream(ServiceLoader.load(Converter.class).spliterator(), false)
                .filter(converter -> converter.accept(sourceType, targetType))
                .findFirst()
                .orElse(null);
    }

}
